package net.jgrm.redisadvanced.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.jgrm.redisadvanced.config.RedisAdvancedProperties;
import net.jgrm.redisadvanced.service.RedisZSetService;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis ZSet类型操作服务实现类
 */
@Slf4j
//@Service
public class RedisZSetServiceImpl implements RedisZSetService {

    //    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //    @Autowired
    private RedisAdvancedProperties redisAdvancedProperties;

    //    @Autowired
    private ObjectMapper objectMapper;

    public RedisZSetServiceImpl(RedisTemplate<String, Object> redisTemplate, RedisAdvancedProperties redisAdvancedProperties, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.redisAdvancedProperties = redisAdvancedProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 添加前缀到键名
     */
    private String addPrefix(String key) {
        return redisAdvancedProperties.getKeyPrefix() + key;
    }

    @Override
    public Boolean add(String key, Object value, double score) {
        try {
            Boolean result = redisTemplate.opsForZSet().add(addPrefix(key), value, score);
            log.debug("ZSet add successful: key={}, score={}", key, score);
            return result;
        } catch (Exception e) {
            log.error("ZSet add error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset add operation failed", e);
        }
    }

    @Override
    public Long add(String key, Map<Object, Double> scoreMembers) {
        // 参数校验
        if (key == null || key.isEmpty()) {
            log.warn("ZSet add multiple failed: key is null or empty");
            throw new IllegalArgumentException("Key must not be null or empty");
        }

        if (scoreMembers == null || scoreMembers.isEmpty()) {
            log.debug("ZSet add multiple skipped: scoreMembers is null or empty, key={}", key);
            return 0L;
        }

        try {
            String prefixedKey = addPrefix(key);
            // 将Map转换为TypedTuple集合
            Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
            for (Map.Entry<Object, Double> entry : scoreMembers.entrySet()) {
                ZSetOperations.TypedTuple<Object> tuple = new DefaultTypedTuple<>(entry.getKey(), entry.getValue());
                tuples.add(tuple);
            }
            Long result = redisTemplate.opsForZSet().add(prefixedKey, tuples);
            log.debug("ZSet add multiple successful: key={}, count={}", key, scoreMembers.size());
            return result;
        } catch (Exception e) {
            log.error("ZSet add multiple error: key={}, scoreMembersCount={}, error={}",
                    key, scoreMembers != null ? scoreMembers.size() : 0, e.getMessage(), e);
            throw new RuntimeException("Redis zset add multiple operation failed", e);
        }
    }


    @Override
    public Long size(String key) {
        try {
            Long result = redisTemplate.opsForZSet().size(addPrefix(key));
            log.debug("ZSet size successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("ZSet size error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset size operation failed", e);
        }
    }

    @Override
    public Long count(String key, double min, double max) {
        try {
            Long result = redisTemplate.opsForZSet().count(addPrefix(key), min, max);
            log.debug("ZSet count successful: key={}, min={}, max={}", key, min, max);
            return result;
        } catch (Exception e) {
            log.error("ZSet count error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset count operation failed", e);
        }
    }

    @Override
    public Double incrementScore(String key, Object value, double delta) {
        try {
            Double result = redisTemplate.opsForZSet().incrementScore(addPrefix(key), value, delta);
            log.debug("ZSet incrementScore successful: key={}, delta={}", key, delta);
            return result;
        } catch (Exception e) {
            log.error("ZSet incrementScore error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset incrementScore operation failed", e);
        }
    }

    @Override
    public Long remove(String key, Object... values) {
        try {
            Long result = redisTemplate.opsForZSet().remove(addPrefix(key), values);
            log.debug("ZSet remove successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("ZSet remove error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset remove operation failed", e);
        }
    }

    @Override
    public <T> Set<T> range(String key, long start, long end, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().range(addPrefix(key), start, end);
            log.debug("ZSet range successful: key={}, start={}, end={}", key, start, end);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet range error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset range operation failed", e);
        }
    }

    @Override
    public <T> Map<T, Double> rangeWithScores(String key, long start, long end, Class<T> clazz) {
        try {
            Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet().rangeWithScores(addPrefix(key), start, end);
            log.debug("ZSet rangeWithScores successful: key={}, start={}, end={}", key, start, end);
            return convertTupleSet(tuples, clazz);
        } catch (Exception e) {
            log.error("ZSet rangeWithScores error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset rangeWithScores operation failed", e);
        }
    }

    @Override
    public <T> Set<T> rangeByScore(String key, double min, double max, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().rangeByScore(addPrefix(key), min, max);
            log.debug("ZSet rangeByScore successful: key={}, min={}, max={}", key, min, max);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet rangeByScore error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset rangeByScore operation failed", e);
        }
    }

    @Override
    public <T> Map<T, Double> rangeByScoreWithScores(String key, double min, double max, Class<T> clazz) {
        try {
            Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet().rangeByScoreWithScores(addPrefix(key), min, max);
            log.debug("ZSet rangeByScoreWithScores successful: key={}, min={}, max={}", key, min, max);
            return convertTupleSet(tuples, clazz);
        } catch (Exception e) {
            log.error("ZSet rangeByScoreWithScores error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset rangeByScoreWithScores operation failed", e);
        }
    }

    @Override
    public <T> Set<T> rangeByScore(String key, double min, double max, long offset, long count, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().rangeByScore(addPrefix(key), min, max, offset, count);
            log.debug("ZSet rangeByScore with offset and count successful: key={}, min={}, max={}", key, min, max);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet rangeByScore with offset and count error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset rangeByScore with offset and count operation failed", e);
        }
    }

    @Override
    public <T> Set<T> reverseRange(String key, long start, long end, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().reverseRange(addPrefix(key), start, end);
            log.debug("ZSet reverseRange successful: key={}, start={}, end={}", key, start, end);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet reverseRange error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRange operation failed", e);
        }
    }

    @Override
    public <T> Map<T, Double> reverseRangeWithScores(String key, long start, long end, Class<T> clazz) {
        try {
            Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(addPrefix(key), start, end);
            log.debug("ZSet reverseRangeWithScores successful: key={}, start={}, end={}", key, start, end);
            return convertTupleSet(tuples, clazz);
        } catch (Exception e) {
            log.error("ZSet reverseRangeWithScores error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRangeWithScores operation failed", e);
        }
    }

    @Override
    public <T> Set<T> reverseRangeByScore(String key, double min, double max, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().reverseRangeByScore(addPrefix(key), min, max);
            log.debug("ZSet reverseRangeByScore successful: key={}, min={}, max={}", key, min, max);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet reverseRangeByScore error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRangeByScore operation failed", e);
        }
    }

    @Override
    public <T> Map<T, Double> reverseRangeByScoreWithScores(String key, double min, double max, Class<T> clazz) {
        try {
            Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(addPrefix(key), min, max);
            log.debug("ZSet reverseRangeByScoreWithScores successful: key={}, min={}, max={}", key, min, max);
            return convertTupleSet(tuples, clazz);
        } catch (Exception e) {
            log.error("ZSet reverseRangeByScoreWithScores error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRangeByScoreWithScores operation failed", e);
        }
    }

    @Override
    public <T> Set<T> reverseRangeByScore(String key, double min, double max, long offset, long count, Class<T> clazz) {
        try {
            Set<Object> values = redisTemplate.opsForZSet().reverseRangeByScore(addPrefix(key), min, max, offset, count);
            log.debug("ZSet reverseRangeByScore with offset and count successful: key={}, min={}, max={}", key, min, max);
            return convertSet(values, clazz);
        } catch (Exception e) {
            log.error("ZSet reverseRangeByScore with offset and count error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRangeByScore with offset and count operation failed", e);
        }
    }

    @Override
    public Long rank(String key, Object value) {
        try {
            Long result = redisTemplate.opsForZSet().rank(addPrefix(key), value);
            log.debug("ZSet rank successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("ZSet rank error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset rank operation failed", e);
        }
    }

    @Override
    public Long reverseRank(String key, Object value) {
        try {
            Long result = redisTemplate.opsForZSet().reverseRank(addPrefix(key), value);
            log.debug("ZSet reverseRank successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("ZSet reverseRank error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset reverseRank operation failed", e);
        }
    }

    @Override
    public Double score(String key, Object value) {
        try {
            Double result = redisTemplate.opsForZSet().score(addPrefix(key), value);
            log.debug("ZSet score successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("ZSet score error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset score operation failed", e);
        }
    }

    @Override
    public Long removeRange(String key, long start, long end) {
        try {
            Long result = redisTemplate.opsForZSet().removeRange(addPrefix(key), start, end);
            log.debug("ZSet removeRange successful: key={}, start={}, end={}", key, start, end);
            return result;
        } catch (Exception e) {
            log.error("ZSet removeRange error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset removeRange operation failed", e);
        }
    }

    @Override
    public Long removeRangeByScore(String key, double min, double max) {
        try {
            Long result = redisTemplate.opsForZSet().removeRangeByScore(addPrefix(key), min, max);
            log.debug("ZSet removeRangeByScore successful: key={}, min={}, max={}", key, min, max);
            return result;
        } catch (Exception e) {
            log.error("ZSet removeRangeByScore error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset removeRangeByScore operation failed", e);
        }
    }

    @Override
    public Boolean expire(String key, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.expire(addPrefix(key), expireTime, timeUnit);
            log.debug("ZSet expire successful: key={}, expireTime={}, timeUnit={}", key, expireTime, timeUnit);
            return result;
        } catch (Exception e) {
            log.error("ZSet expire error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis zset expire operation failed", e);
        }
    }

    /**
     * 将单个对象转换为指定类型
     */
    private <T> T convertValue(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return clazz.cast(value);
        } else {
            return objectMapper.convertValue(value, clazz);
        }
    }

    /**
     * 将对象Set转换为指定类型的Set
     */
    private <T> Set<T> convertSet(Set<Object> values, Class<T> clazz) {
        Set<T> result = new HashSet<>(values.size());
        for (Object value : values) {
            result.add(convertValue(value, clazz));
        }
        return result;
    }

    /**
     * 将TypedTuple Set转换为指定类型的Map
     */
    private <T> Map<T, Double> convertTupleSet(Set<ZSetOperations.TypedTuple<Object>> tuples, Class<T> clazz) {
        Map<T, Double> result = new HashMap<>(tuples.size());
        for (ZSetOperations.TypedTuple<Object> tuple : tuples) {
            Object value = tuple.getValue();
            if (value != null) {
                T convertedValue = convertValue(value, clazz);
                result.put(convertedValue, tuple.getScore());
            }
        }
        return result;
    }
}